﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Notification;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Xml.Linq;
using System.IO;
using System.Windows.Threading;
using System.Windows.Media.Imaging;
using System.IO.IsolatedStorage;
using Microsoft.Phone.Tasks;
using Microsoft.Phone.Shell;

namespace DevCorner.Weather.WP7
{
  public partial class MainPage : PhoneApplicationPage
  {
    private HttpNotificationChannel httpChannel;
    EmailAddressChooserTask emailAddressChooserTask;
    const string channelName = "DevCorner.WeatherChannel";
    const string fileName = "DevCorner.WeatherSettings.dat";
    const int pushConnectTimeout = 30;


    // Constructor
    public MainPage()
    {
      InitializeComponent();

      SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;
      emailAddressChooserTask = new EmailAddressChooserTask();
      emailAddressChooserTask.Completed += new EventHandler<EmailResult>(emailAddressChooserTask_Completed);
      if (!TryFindChannel())
        DoConnect();
    }

    void emailAddressChooserTask_Completed(object sender, EmailResult e)
    {
      if (e.TaskResult == TaskResult.OK)
      {
        EmailComposeTask emailComposeTask = new EmailComposeTask();
        emailComposeTask.To = e.Email;
        emailComposeTask.Subject = "Weather Sample";
        emailComposeTask.Body = "Hi!\nJust wanted to share, how good Weather sample is!\n\nTake care!";
        emailComposeTask.Show();
      }
    }

    #region Channel event handlers
    void httpChannel_ExceptionOccurred(object sender, NotificationChannelErrorEventArgs e)
    {
      Dispatcher.BeginInvoke(() => UpdateStatus(e.ErrorType + " occurred: " + e.Message));
    }

    void httpChannel_ShellToastNotificationReceived(object sender, NotificationEventArgs e)
    {
      Trace("===============================================");
      Trace("Toast/Tile notification arrived:");
      foreach (var key in e.Collection.Keys)
      {
        string msg = e.Collection[key];

        Trace(msg);
        Dispatcher.BeginInvoke(() => UpdateStatus("Toast/Tile message: " + msg));
      }
      Trace("===============================================");
    }

    void httpChannel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)
    {
      Trace("===============================================");
      Trace("RAW notification arrived:");

      string weather, location, temperature;
      ParseRAWPayload(e.Notification.Body, out weather, out location, out temperature);

      Dispatcher.BeginInvoke(() => this.PageTitle.Text = location);
      Dispatcher.BeginInvoke(() => this.txtTemperature.Text = temperature);
      Dispatcher.BeginInvoke(() => this.imgWeatherConditions.Source = new BitmapImage(new Uri(@"Images/" + weather + ".png", UriKind.Relative)));
      Trace(string.Format("Got weather: {0} with {1}F at location {2}", weather, temperature, location));

      Trace("===============================================");
    }

    void httpChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
    {
      Trace("Channel opened. Got Uri:\n" + httpChannel.ChannelUri.ToString());
      Dispatcher.BeginInvoke(() => SaveChannelInfo());
      Trace("Subscribing to channel events");
      SubscribeToService();
      SubscribeToNotifications();
      Dispatcher.BeginInvoke(() => UpdateStatus("Channel created successfully"));
      Dispatcher.BeginInvoke(() => this.ApplicationBar.IsVisible = true);
    }
    #endregion

    #region Misc logic
    private void DoConnect()
    {
      try
      {
        //First, try to pick up existing channel
        httpChannel = HttpNotificationChannel.Find(channelName);

        if (null != httpChannel)
        {
          Trace("Got existing channel");
          SubscribeToChannelEvents();

          Trace("Subscribing to channel events");
          SubscribeToService();
          SubscribeToNotifications();

          Dispatcher.BeginInvoke(() => UpdateStatus("Channel recovered"));
        }
        else
        {
          Trace("Creating new channel");
          //Create the channel
          httpChannel = new HttpNotificationChannel(channelName, "HOLWeatherService");

          Trace("Subscribing to channel events");
          //Register to UriUpdated event - occurs when channel successfully opens
          httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(httpChannel_ChannelUriUpdated);

          SubscribeToChannelEvents();

          Trace("Opening channel");
          httpChannel.Open();
          Dispatcher.BeginInvoke(() => UpdateStatus("Channel open requested"));
        }
      }
      catch (Exception ex)
      {
        if (ex.Message == "Notification server temporary unavailable")
        {
          Trace("EMU workaround - wait for " + pushConnectTimeout.ToString() + " seconds to allow emulator connect to the Cloud Push Service");
          //Wait for emulator to connect to the push service and try-again - EMULATOR ONLY!!!
          int elapsedSeconds = pushConnectTimeout;
          DispatcherTimer timer = new DispatcherTimer();
          timer.Interval = new TimeSpan(0, 0, 0, 1, 0);
          timer.Tick += (sender, args) =>
          {
            Dispatcher.BeginInvoke(() => UpdateStatus("Waiting for push service for " + elapsedSeconds.ToString() + " seconds..."));
            if (--elapsedSeconds == 0)
            {
              timer.Stop();
              Dispatcher.BeginInvoke(() => UpdateStatus("Plase restart the application..."));
              Trace("EMU workaround - finish waiting. Restart the application!");
            }
          };
          timer.Start();
        }
        else
          Dispatcher.BeginInvoke(() => UpdateStatus("Channel error: " + ex.Message));
      }
    }

    private void ParseRAWPayload(Stream e, out string weather, out string location, out string temperature)
    {
      XDocument document;

      using (var reader = new StreamReader(e))
      {
        string payload = reader.ReadToEnd().Replace('\0', ' ');
        document = XDocument.Parse(payload);
      }

      location = (from c in document.Descendants("WeatherUpdate")
                  select c.Element("Location").Value).FirstOrDefault();
      Trace("Got location: " + location);

      temperature = (from c in document.Descendants("WeatherUpdate")
                     select c.Element("Temperature").Value).FirstOrDefault();
      Trace("Got temperature: " + temperature);

      weather = (from c in document.Descendants("WeatherUpdate")
                 select c.Element("WeatherType").Value).FirstOrDefault();
    }
    #endregion

    #region Tracing and Status Updates
    private void UpdateStatus(string message)
    {
      txtStatus.Text = message;
    }

    private void Trace(string message)
    {
#if DEBUG
      Debug.WriteLine(message);
#endif
    }
    #endregion

    #region Loading/Saving Channel Info
    private bool TryFindChannel()
    {
      bool bRes = false;

      Trace("Getting IsolatedStorage for current Application");
      using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
      {
        Trace("Checking channel data");
        if (isf.FileExists(fileName))
        {
          Trace("Channel data exists! Loading...");
          using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fileName, FileMode.Open, isf))
          {
            using (StreamReader sr = new StreamReader(isfs))
            {
              string uri = sr.ReadLine();
              Trace("Finding channel");
              httpChannel = HttpNotificationChannel.Find(channelName);

              if (null != httpChannel)
              {
                if (httpChannel.ChannelUri.ToString() == uri)
                {
                  Dispatcher.BeginInvoke(() => UpdateStatus("Channel retrieved"));
                  Dispatcher.BeginInvoke(() => this.ApplicationBar.IsVisible = true);
                  SubscribeToChannelEvents();
                  SubscribeToNotifications();
                  SubscribeToService();
                  bRes = true;
                }
                sr.Close();
              }
            }
          }
        }
        else
          Trace("Channel data not found");
      }

      return bRes;
    }

    public void SaveChannelInfo()
    {
      Trace("Getting IsolatedStorage for current Application");
      using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
      {
        Trace("Creating data file");
        using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fileName, FileMode.Create, isf))
        {
          using (StreamWriter sw = new StreamWriter(isfs))
          {
            Trace("Saving channel data...");
            sw.WriteLine(httpChannel.ChannelUri.ToString());
            sw.Close();
            Trace("Saving done");
          }
        }
      }
    }
    #endregion

    #region Subscriptions
    private void SubscribeToService()
    {
      RegistrationSVC.RegistrationServiceClient client = new RegistrationSVC.RegistrationServiceClient();
      client.RegisterCompleted += (s, e) =>
        {
          if (null == e.Error)
            Dispatcher.BeginInvoke(() => UpdateStatus("Registration succeeded"));
          else
            Dispatcher.BeginInvoke(() => UpdateStatus("Registration failed: " + e.Error.Message));
        };
      client.RegisterAsync(httpChannel.ChannelUri.ToString());
    }

    private void SubscribeToNotifications()
    {
      try
      {
        Trace("Registering to Toast Notifications");
        httpChannel.BindToShellToast();
      }
      catch (Exception ex)
      {
        //Nothing to do - channel already exists and subscribed
      }

      try
      {
        Trace("Registering to Tile Notifications");
        Collection<Uri> uris = new Collection<Uri>();
        uris.Add(new Uri("http://devcorner.info/Notifications/DevCorner.png"));

        httpChannel.BindToShellTile(uris);
      }
      catch (Exception ex)
      {
        //Nothing to do - channel already exists and subscribed
      }

    }

    private void SubscribeToChannelEvents()
    {
      //Subscribe to the channel events
      httpChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(httpChannel_HttpNotificationReceived);
      httpChannel.ShellToastNotificationReceived += new EventHandler<NotificationEventArgs>(httpChannel_ShellToastNotificationReceived);
      httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(httpChannel_ExceptionOccurred);
    }

    #endregion

    #region Tombstoning
    protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
    {
      if (PageTitle.Text.Length > 0)
        this.State.Add("LastLocation", PageTitle.Text);

      if (PageTitle.Text.Length > 0)
        this.State.Add("LastTemperature", txtTemperature.Text);

      if (null != imgWeatherConditions.Source)
        this.State.Add("LastWeather", (imgWeatherConditions.Source as BitmapImage).UriSource.ToString());
      base.OnNavigatedFrom(e);
    }

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
      if (this.State.ContainsKey("LastLocation"))
        PageTitle.Text = this.State["LastLocation"].ToString();

      if (this.State.ContainsKey("LastTemperature"))
        txtTemperature.Text = this.State["LastTemperature"].ToString();

      if (this.State.ContainsKey("LastWeather"))
      {
        BitmapImage img = new BitmapImage(new Uri(this.State["LastWeather"].ToString(), UriKind.Relative));
        imgWeatherConditions.Source = img;
      }
      base.OnNavigatedTo(e);
    }

    protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
    {
      if (null != httpChannel.ChannelUri)
        SaveChannelInfo();

      base.OnBackKeyPress(e);
    }
    #endregion

    #region Private functionality & event handlers
    private void menuItemDebug_Click(object sender, EventArgs e)
    {
      string theUri = "DISCONNECTED";

      if (null != httpChannel.ChannelUri)
        theUri = httpChannel.ChannelUri.ToString();

      MessageBox.Show("Active Channel URI:\n" + theUri, "Debug Info", MessageBoxButton.OK);
    }

    private void menuItemAbout_Click(object sender, EventArgs e)
    {
      NavigationService.Navigate(new Uri("/Pages/About.xaml", UriKind.Relative));
    }

    private void menuItemAddEmail_Click(object sender, EventArgs e)
    {
      NavigationService.Navigate(new Uri("/Pages/SaveEmail.xaml", UriKind.Relative));
    }

    private void appbarBtnShowMenu_Click(object sender, EventArgs e)
    {
      this.ApplicationBar.IsMenuEnabled = !this.ApplicationBar.IsMenuEnabled;

      (this.ApplicationBar.Buttons[0] as ApplicationBarIconButton).Text = this.ApplicationBar.IsMenuEnabled ? "Hide Menu" : "Show Menu";
    }
    #endregion

    #region Choosers & Launchers
    private void appbarBtnSendSMS_Click(object sender, EventArgs e)
    {
      emailAddressChooserTask.Show();
    }

    private void appbarBtnSearch_Click(object sender, EventArgs e)
    {
      SearchTask searchTask = new SearchTask();
      searchTask.SearchQuery = "Alex Golesh";
      searchTask.Show();
    }
    #endregion

  }
}
